#include "sockets_s.h"
#include "../../Mensajes.h"

// Semáforo del thread socket
extern sem_t mutex_socket;

// Semáforo del thread socket
extern sem_t mutex_socket_s;
extern sem_t socket_disp;
extern sem_t cliente_conectado;

// Semáforos movimientos
extern sem_t nuevo_mov;
extern sem_t sem_mov;
extern sem_t mov_valido;

// Mensaje inicio
extern Mensaje msj_inicio;

// Flag estado socket servidor (RECV,SEND,INICIO,CONF)
int flag=0;

// Error en envío/recepción
extern int socket_error;

// Jugador de turno
extern DatosJugador* JugadorTurno;

// Thread socket en servidor
void* thread_socket_s(void* arg)
{
	int value=-1;
	
	sv_config config;
	s_mov* mov = (s_mov*) arg;
	s_mov buffer;
	
	// Cargar configuración
	configurar_servidor(&config);
	
	// Thread libre
	sem_post(&socket_disp);
	
	while(1)
	{
		// Espero desbloqueo
		lock_socket_s();
		
		if(flag == INICIO)									// Envío mensaje inicio a clientes
		{
			mov->estado = ACK;
			if(send(JugadorTurno->Descriptor,mov,sizeof(s_mov),0) == -1)
    	{ // Jugador desconectado
				JugadorTurno->Conectado = false;
			}
    				
			if(send(JugadorTurno->Descriptor,&msj_inicio,sizeof(Mensaje),0) == -1)
			{ // Jugador desconectado
				JugadorTurno->Conectado = false;
			}
						
			flag=-1;
		
			// Thread libre
			sem_post(&socket_disp);
			sem_post(&nuevo_mov);
		}
		
		else
		{
			if(flag == CONF)									// Confirmación movimientos
			{				
				// Copio movimiento
				buffer.accion = mov->accion;
				buffer.estado = mov->estado;
				buffer.posicion = mov->posicion;
				buffer.n_posicion = mov->n_posicion;
				
				// Mutex movimiento
				sem_post(&sem_mov);
				
				if(send(JugadorTurno->Descriptor,&buffer,sizeof(s_mov),0) == -1)
				{ // Jugador desconectado
					JugadorTurno->Conectado = false;
				}
							
				flag=-1;
			
				// Thread libre
				sem_post(&socket_disp);
				
				// A modo de confirmación
				sem_post(&nuevo_mov);	
			}
			
			else 															// Envío y recepción
			{				
				// Mutex movimiento
				sem_wait(&sem_mov);
				
				if(send(JugadorTurno->Descriptor,mov,sizeof(s_mov),0) == -1)
    		{ // Jugador desconectado
					JugadorTurno->Conectado = false;
				}
				
				if(recv(JugadorTurno->Descriptor,mov,sizeof(s_mov),0) <= 0)
    		{ // Jugador desconectado
					JugadorTurno->Conectado = false;
				}
				
				// A modo de confirmación
				sem_post(&nuevo_mov);	
				
				flag=-1;
				
				// Thread libre
				sem_post(&socket_disp);
				
				// Mutex movimiento
				sem_post(&sem_mov);
			}
		}
	}
}

// Funciones adicionales

// Bloquear socket server
void lock_socket_s()
{
	sem_wait(&mutex_socket_s);
}

// Desbloquear socket server, recibe flag y descriptor
void unlock_socket_s(int est, DatosJugador* jug)
{
	// Thread ocupado
	sem_wait(&socket_disp);
	
	// Flag estado
	flag = est;
	
	// Puntero a jugador del turno
	JugadorTurno = jug;
	
	sem_post(&mutex_socket_s);
}

void configurar_servidor(sv_config* arg)
{
	FILE* arch;
	
	if((arch = fopen("config/server_cfg","rt")) == NULL)
	{
		printf("No se puede leer configuración\n");
		exit(1);
	}
	
	fscanf(arch,"PUERTO = %d\nPARTIDAS = %d",&(arg->puerto),&(arg->cant_partidas));
}

void iniciar_socket(int* sock,sv_config* configuracion) 
{
	// Funcion que inicializa el socket del servidor por unica vez	
	int val=1;
	struct sockaddr_in srv;
	
	// Borrar
	struct sockaddr_in socketdir_cliente1,socketdir_cliente2; 
	int addrlen = sizeof( (struct sockaddr *) &socketdir_cliente1);
	
	// Crear socket
	if((*sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
		error("Error en creación de socket\n");
	
	// Definir propiedades del socket
	srv.sin_family = AF_INET;
	srv.sin_port = htons(configuracion->puerto);
	srv.sin_addr.s_addr = inet_addr(HOST);	

	// Reuse
	if(setsockopt(*sock,SOL_SOCKET,SO_REUSEADDR,&val,sizeof(int)) == -1)
		error("Error en reuse\n");
	
	// Vincular socket
	if(bind(*sock,(struct sockaddr*)&srv,sizeof(struct sockaddr)) <0)
	{
		close(*sock);
		error("Error en bind\n");
	}

	// Escuchar
	if(listen(*sock,2) <0)
	{
		close(*sock);
		error("Error en listen\n");
	}
}
